/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/*
 * cvsamples.cpp
 *
 * support functions for training and test samples creation.
 */

#include "cvhaartraining.h"
#include "_cvhaartraining.h"

/* if ipl.h file is included then iplWarpPerspectiveQ function
   is used for image transformation during samples creation;
   otherwise internal cvWarpPerspective function is used */

//#include <ipl.h>

#include <cv.h>
#include <highgui.h>

/* Calculates coefficients of perspective transformation
 * which maps <quad> into rectangle ((0,0), (w,0), (w,h), (h,0)):
 *
 *      c00*xi + c01*yi + c02
 * ui = ---------------------
 *      c20*xi + c21*yi + c22
 *
 *      c10*xi + c11*yi + c12
 * vi = ---------------------
 *      c20*xi + c21*yi + c22
 *
 * Coefficients are calculated by solving linear system:
 * / x0 y0  1  0  0  0 -x0*u0 -y0*u0 \ /c00\ /u0\
 * | x1 y1  1  0  0  0 -x1*u1 -y1*u1 | |c01| |u1|
 * | x2 y2  1  0  0  0 -x2*u2 -y2*u2 | |c02| |u2|
 * | x3 y3  1  0  0  0 -x3*u3 -y3*u3 |.|c10|=|u3|,
 * |  0  0  0 x0 y0  1 -x0*v0 -y0*v0 | |c11| |v0|
 * |  0  0  0 x1 y1  1 -x1*v1 -y1*v1 | |c12| |v1|
 * |  0  0  0 x2 y2  1 -x2*v2 -y2*v2 | |c20| |v2|
 * \  0  0  0 x3 y3  1 -x3*v3 -y3*v3 / \c21/ \v3/
 *
 * where:
 *   (xi, yi) = (quad[i][0], quad[i][1])
 *        cij - coeffs[i][j], coeffs[2][2] = 1
 *   (ui, vi) - rectangle vertices
 */
void cvGetPerspectiveTransform(CvSize src_size, double quad[4][2],
                               double coeffs[3][3]) {
    //CV_FUNCNAME( "cvWarpPerspective" );

    __BEGIN__;

    double a[8][8];
    double b[8];

    CvMat A = cvMat(8, 8, CV_64FC1, a);
    CvMat B = cvMat(8, 1, CV_64FC1, b);
    CvMat X = cvMat(8, 1, CV_64FC1, coeffs);

    int i;
    for (i = 0; i < 4; ++i) {
        a[i][0] = quad[i][0]; a[i][1] = quad[i][1]; a[i][2] = 1;
        a[i][3] = a[i][4] = a[i][5] = a[i][6] = a[i][7] = 0;
        b[i] = 0;
    }
    for (i = 4; i < 8; ++i) {
        a[i][3] = quad[i - 4][0]; a[i][4] = quad[i - 4][1]; a[i][5] = 1;
        a[i][0] = a[i][1] = a[i][2] = a[i][6] = a[i][7] = 0;
        b[i] = 0;
    }

    int u = src_size.width - 1;
    int v = src_size.height - 1;

    a[1][6] = -quad[1][0] * u; a[1][7] = -quad[1][1] * u;
    a[2][6] = -quad[2][0] * u; a[2][7] = -quad[2][1] * u;
    b[1] = b[2] = u;

    a[6][6] = -quad[2][0] * v; a[6][7] = -quad[2][1] * v;
    a[7][6] = -quad[3][0] * v; a[7][7] = -quad[3][1] * v;
    b[6] = b[7] = v;

    cvSolve(&A, &B, &X);

    coeffs[2][2] = 1;

    __END__;
}

/* Warps source into destination by a perspective transform */
void cvWarpPerspective(CvArr* src, CvArr* dst, double quad[4][2]) {
    CV_FUNCNAME("cvWarpPerspective");

    __BEGIN__;

#ifdef __IPL_H__
    IplImage src_stub, dst_stub;
    IplImage* src_img;
    IplImage* dst_img;
    CV_CALL(src_img = cvGetImage(src, &src_stub));
    CV_CALL(dst_img = cvGetImage(dst, &dst_stub));
    iplWarpPerspectiveQ(src_img, dst_img, quad, IPL_WARP_R_TO_Q,
                        IPL_INTER_CUBIC | IPL_SMOOTH_EDGE);
#else

    int fill_value = 0;

    double c[3][3]; /* transformation coefficients */
    double q[4][2]; /* rearranged quad */

    int left = 0;
    int right = 0;
    int next_right = 0;
    int next_left = 0;
    double y_min = 0;
    double y_max = 0;
    double k_left, b_left, k_right, b_right;

    uchar* src_data;
    int src_step;
    CvSize src_size;

    uchar* dst_data;
    int dst_step;
    CvSize dst_size;

    double d = 0;
    int direction = 0;
    int i;

    if (!src || (!CV_IS_IMAGE(src) && !CV_IS_MAT(src)) ||
            cvGetElemType(src) != CV_8UC1 ||
            cvGetDims(src) != 2) {
        CV_ERROR(CV_StsBadArg,
                 "Source must be two-dimensional array of CV_8UC1 type.");
    }
    if (!dst || (!CV_IS_IMAGE(dst) && !CV_IS_MAT(dst)) ||
            cvGetElemType(dst) != CV_8UC1 ||
            cvGetDims(dst) != 2) {
        CV_ERROR(CV_StsBadArg,
                 "Destination must be two-dimensional array of CV_8UC1 type.");
    }

    CV_CALL(cvGetRawData(src, &src_data, &src_step, &src_size));
    CV_CALL(cvGetRawData(dst, &dst_data, &dst_step, &dst_size));

    CV_CALL(cvGetPerspectiveTransform(src_size, quad, c));

    /* if direction > 0 then vertices in quad follow in a CW direction,
       otherwise they follow in a CCW direction */
    direction = 0;
    for (i = 0; i < 4; ++i) {
        int ni = i + 1; if (ni == 4) { ni = 0; }
        int pi = i - 1; if (pi == -1) { pi = 3; }

        d = (quad[i][0] - quad[pi][0]) * (quad[ni][1] - quad[i][1]) -
            (quad[i][1] - quad[pi][1]) * (quad[ni][0] - quad[i][0]);
        int cur_direction = CV_SIGN(d);
        if (direction == 0) {
            direction = cur_direction;
        } else if (direction * cur_direction < 0) {
            direction = 0;
            break;
        }
    }
    if (direction == 0) {
        CV_ERROR(CV_StsBadArg, "Quadrangle is nonconvex or degenerated.");
    }

    /* <left> is the index of the topmost quad vertice
       if there are two such vertices <left> is the leftmost one */
    left = 0;
    for (i = 1; i < 4; ++i) {
        if ((quad[i][1] < quad[left][1]) ||
                ((quad[i][1] == quad[left][1]) && (quad[i][0] < quad[left][0]))) {
            left = i;
        }
    }
    /* rearrange <quad> vertices in such way that they follow in a CW
       direction and the first vertice is the topmost one and put them
       into <q> */
    if (direction > 0) {
        for (i = left; i < 4; ++i) {
            q[i - left][0] = quad[i][0];
            q[i - left][1] = quad[i][1];
        }
        for (i = 0; i < left; ++i) {
            q[4 - left + i][0] = quad[i][0];
            q[4 - left + i][1] = quad[i][1];
        }
    } else {
        for (i = left; i >= 0; --i) {
            q[left - i][0] = quad[i][0];
            q[left - i][1] = quad[i][1];
        }
        for (i = 3; i > left; --i) {
            q[4 + left - i][0] = quad[i][0];
            q[4 + left - i][1] = quad[i][1];
        }
    }

    left = right = 0;
    /* if there are two topmost points, <right> is the index of the rightmost one
       otherwise <right> */
    if (q[left][1] == q[left + 1][1]) {
        right = 1;
    }

    /* <next_left> follows <left> in a CCW direction */
    next_left = 3;
    /* <next_right> follows <right> in a CW direction */
    next_right = right + 1;

    /* subtraction of 1 prevents skipping of the first row */
    y_min = q[left][1] - 1;

    /* left edge equation: y = k_left * x + b_left */
    k_left = (q[left][0] - q[next_left][0]) /
             (q[left][1] - q[next_left][1]);
    b_left = (q[left][1] * q[next_left][0] -
              q[left][0] * q[next_left][1]) /
             (q[left][1] - q[next_left][1]);

    /* right edge equation: y = k_right * x + b_right */
    k_right = (q[right][0] - q[next_right][0]) /
              (q[right][1] - q[next_right][1]);
    b_right = (q[right][1] * q[next_right][0] -
               q[right][0] * q[next_right][1]) /
              (q[right][1] - q[next_right][1]);

    for (;;) {
        int x, y;

        y_max = MIN(q[next_left][1], q[next_right][1]);

        int iy_min = MAX(cvRound(y_min), 0) + 1;
        int iy_max = MIN(cvRound(y_max), dst_size.height - 1);

        double x_min = k_left * iy_min + b_left;
        double x_max = k_right * iy_min + b_right;

        /* walk through the destination quadrangle row by row */
        for (y = iy_min; y <= iy_max; ++y) {
            int ix_min = MAX(cvRound(x_min), 0);
            int ix_max = MIN(cvRound(x_max), dst_size.width - 1);

            for (x = ix_min; x <= ix_max; ++x) {
                /* calculate coordinates of the corresponding source array point */
                double div = (c[2][0] * x + c[2][1] * y + c[2][2]);
                double src_x = (c[0][0] * x + c[0][1] * y + c[0][2]) / div;
                double src_y = (c[1][0] * x + c[1][1] * y + c[1][2]) / div;

                int isrc_x = cvFloor(src_x);
                int isrc_y = cvFloor(src_y);
                double delta_x = src_x - isrc_x;
                double delta_y = src_y - isrc_y;

                uchar* s = src_data + isrc_y * src_step + isrc_x;

                int i00, i10, i01, i11;
                i00 = i10 = i01 = i11 = (int) fill_value;

                double i = fill_value;

                /* linear interpolation using 2x2 neighborhood */
                if (isrc_x >= 0 && isrc_x <= src_size.width &&
                        isrc_y >= 0 && isrc_y <= src_size.height) {
                    i00 = s[0];
                }
                if (isrc_x >= -1 && isrc_x < src_size.width &&
                        isrc_y >= 0 && isrc_y <= src_size.height) {
                    i10 = s[1];
                }
                if (isrc_x >= 0 && isrc_x <= src_size.width &&
                        isrc_y >= -1 && isrc_y < src_size.height) {
                    i01 = s[src_step];
                }
                if (isrc_x >= -1 && isrc_x < src_size.width &&
                        isrc_y >= -1 && isrc_y < src_size.height) {
                    i11 = s[src_step + 1];
                }

                double i0 = i00 + (i10 - i00) * delta_x;
                double i1 = i01 + (i11 - i01) * delta_x;
                i = i0 + (i1 - i0) * delta_y;

                ((uchar*)(dst_data + y * dst_step))[x] = (uchar) i;
            }
            x_min += k_left;
            x_max += k_right;
        }

        if ((next_left == next_right) ||
                (next_left + 1 == next_right && q[next_left][1] == q[next_right][1])) {
            break;
        }

        if (y_max == q[next_left][1]) {
            left = next_left;
            next_left = left - 1;

            k_left = (q[left][0] - q[next_left][0]) /
                     (q[left][1] - q[next_left][1]);
            b_left = (q[left][1] * q[next_left][0] -
                      q[left][0] * q[next_left][1]) /
                     (q[left][1] - q[next_left][1]);
        }
        if (y_max == q[next_right][1]) {
            right = next_right;
            next_right = right + 1;

            k_right = (q[right][0] - q[next_right][0]) /
                      (q[right][1] - q[next_right][1]);
            b_right = (q[right][1] * q[next_right][0] -
                       q[right][0] * q[next_right][1]) /
                      (q[right][1] - q[next_right][1]);
        }
        y_min = y_max;
    }
#endif /* #ifndef __IPL_H__ */

    __END__;
}

static
void icvRandomQuad(int width, int height, double quad[4][2],
                   double maxxangle,
                   double maxyangle,
                   double maxzangle) {
    double distfactor = 3.0;
    double distfactor2 = 1.0;

    double halfw, halfh;
    int i;

    double rotVectData[3];
    double vectData[3];
    double rotMatData[9];

    CvMat rotVect;
    CvMat rotMat;
    CvMat vect;

    double d;

    rotVect = cvMat(3, 1, CV_64FC1, &rotVectData[0]);
    rotMat = cvMat(3, 3, CV_64FC1, &rotMatData[0]);
    vect = cvMat(3, 1, CV_64FC1, &vectData[0]);

    rotVectData[0] = maxxangle * (2.0 * rand() / RAND_MAX - 1.0);
    rotVectData[1] = (maxyangle - fabs(rotVectData[0]))
                     * (2.0 * rand() / RAND_MAX - 1.0);
    rotVectData[2] = maxzangle * (2.0 * rand() / RAND_MAX - 1.0);
    d = (distfactor + distfactor2 * (2.0 * rand() / RAND_MAX - 1.0)) * width;

    /*
        rotVectData[0] = maxxangle;
        rotVectData[1] = maxyangle;
        rotVectData[2] = maxzangle;

        d = distfactor * width;
    */

    cvRodrigues2(&rotMat, &rotVect);

    halfw = 0.5 * width;
    halfh = 0.5 * height;

    quad[0][0] = -halfw;
    quad[0][1] = -halfh;
    quad[1][0] =  halfw;
    quad[1][1] = -halfh;
    quad[2][0] =  halfw;
    quad[2][1] =  halfh;
    quad[3][0] = -halfw;
    quad[3][1] =  halfh;

    for (i = 0; i < 4; i++) {
        rotVectData[0] = quad[i][0];
        rotVectData[1] = quad[i][1];
        rotVectData[2] = 0.0;
        cvMatMulAdd(&rotMat, &rotVect, 0, &vect);
        quad[i][0] = vectData[0] * d / (d + vectData[2]) + halfw;
        quad[i][1] = vectData[1] * d / (d + vectData[2]) + halfh;

        /*
        quad[i][0] += halfw;
        quad[i][1] += halfh;
        */
    }
}


int icvStartSampleDistortion(const char* imgfilename, int bgcolor, int bgthreshold,
                             CvSampleDistortionData* data) {
    memset(data, 0, sizeof(*data));
    data->src = cvLoadImage(imgfilename, 0);
    if (data->src != NULL && data->src->nChannels == 1
            && data->src->depth == IPL_DEPTH_8U) {
        int r, c;
        uchar* pmask;
        uchar* psrc;
        uchar* perode;
        uchar* pdilate;
        uchar dd, de;

        data->dx = data->src->width / 2;
        data->dy = data->src->height / 2;
        data->bgcolor = bgcolor;

        data->mask = cvCloneImage(data->src);
        data->erode = cvCloneImage(data->src);
        data->dilate = cvCloneImage(data->src);

        /* make mask image */
        for (r = 0; r < data->mask->height; r++) {
            for (c = 0; c < data->mask->width; c++) {
                pmask = ((uchar*)(data->mask->imageData + r * data->mask->widthStep)
                         + c);
                if (bgcolor - bgthreshold <= (int)(*pmask) &&
                        (int)(*pmask) <= bgcolor + bgthreshold) {
                    *pmask = (uchar) 0;
                } else {
                    *pmask = (uchar) 255;
                }
            }
        }

        /* extend borders of source image */
        cvErode(data->src, data->erode, 0, 1);
        cvDilate(data->src, data->dilate, 0, 1);
        for (r = 0; r < data->mask->height; r++) {
            for (c = 0; c < data->mask->width; c++) {
                pmask = ((uchar*)(data->mask->imageData + r * data->mask->widthStep)
                         + c);
                if ((*pmask) == 0) {
                    psrc = ((uchar*)(data->src->imageData + r * data->src->widthStep)
                            + c);
                    perode =
                        ((uchar*)(data->erode->imageData + r * data->erode->widthStep)
                         + c);
                    pdilate =
                        ((uchar*)(data->dilate->imageData + r * data->dilate->widthStep)
                         + c);
                    de = (uchar)(bgcolor - (*perode));
                    dd = (uchar)((*pdilate) - bgcolor);
                    if (de >= dd && de > bgthreshold) {
                        (*psrc) = (*perode);
                    }
                    if (dd > de && dd > bgthreshold) {
                        (*psrc) = (*pdilate);
                    }
                }
            }
        }

        data->img = cvCreateImage(cvSize(data->src->width + 2 * data->dx,
                                         data->src->height + 2 * data->dy),
                                  IPL_DEPTH_8U, 1);
        data->maskimg = cvCloneImage(data->img);

        return 1;
    }

    return 0;
}

void icvPlaceDistortedSample(CvArr* background,
                             int inverse, int maxintensitydev,
                             double maxxangle, double maxyangle, double maxzangle,
                             int inscribe, double maxshiftf, double maxscalef,
                             CvSampleDistortionData* data) {
    double quad[4][2];
    int r, c;
    uchar* pimg;
    uchar* pbg;
    uchar* palpha;
    uchar chartmp;
    int forecolordev;
    float scale;
    IplImage* img;
    IplImage* maskimg;
    CvMat  stub;
    CvMat* bgimg;

    CvRect cr;
    CvRect roi;

    double xshift, yshift, randscale;

    icvRandomQuad(data->src->width, data->src->height, quad,
                  maxxangle, maxyangle, maxzangle);
    quad[0][0] += (double) data->dx;
    quad[0][1] += (double) data->dy;
    quad[1][0] += (double) data->dx;
    quad[1][1] += (double) data->dy;
    quad[2][0] += (double) data->dx;
    quad[2][1] += (double) data->dy;
    quad[3][0] += (double) data->dx;
    quad[3][1] += (double) data->dy;

    cvSet(data->img, cvScalar(data->bgcolor));
    cvSet(data->maskimg, cvScalar(0.0));

    cvWarpPerspective(data->src, data->img, quad);
    cvWarpPerspective(data->mask, data->maskimg, quad);

    cvSmooth(data->maskimg, data->maskimg, CV_GAUSSIAN, 3, 3);

    bgimg = cvGetMat(background, &stub);

    cr.x = data->dx;
    cr.y = data->dy;
    cr.width = data->src->width;
    cr.height = data->src->height;

    if (inscribe) {
        /* quad's circumscribing rectangle */
        cr.x = (int) MIN(quad[0][0], quad[3][0]);
        cr.y = (int) MIN(quad[0][1], quad[1][1]);
        cr.width  = (int)(MAX(quad[1][0], quad[2][0]) + 0.5F) - cr.x;
        cr.height = (int)(MAX(quad[2][1], quad[3][1]) + 0.5F) - cr.y;
    }

    xshift = maxshiftf * rand() / RAND_MAX;
    yshift = maxshiftf * rand() / RAND_MAX;

    cr.x -= (int)(xshift * cr.width);
    cr.y -= (int)(yshift * cr.height);
    cr.width  = (int)((1.0 + maxshiftf) * cr.width);
    cr.height = (int)((1.0 + maxshiftf) * cr.height);

    randscale = maxscalef * rand() / RAND_MAX;
    cr.x -= (int)(0.5 * randscale * cr.width);
    cr.y -= (int)(0.5 * randscale * cr.height);
    cr.width  = (int)((1.0 + randscale) * cr.width);
    cr.height = (int)((1.0 + randscale) * cr.height);

    scale = MAX(((float) cr.width) / bgimg->cols, ((float) cr.height) / bgimg->rows);

    roi.x = (int)(-0.5F * (scale * bgimg->cols - cr.width) + cr.x);
    roi.y = (int)(-0.5F * (scale * bgimg->rows - cr.height) + cr.y);
    roi.width  = (int)(scale * bgimg->cols);
    roi.height = (int)(scale * bgimg->rows);

    img = cvCreateImage(cvSize(bgimg->cols, bgimg->rows), IPL_DEPTH_8U, 1);
    maskimg = cvCreateImage(cvSize(bgimg->cols, bgimg->rows), IPL_DEPTH_8U, 1);

    cvSetImageROI(data->img, roi);
    cvResize(data->img, img);
    cvResetImageROI(data->img);
    cvSetImageROI(data->maskimg, roi);
    cvResize(data->maskimg, maskimg);
    cvResetImageROI(data->maskimg);

    forecolordev = (int)(maxintensitydev * (2.0 * rand() / RAND_MAX - 1.0));

    for (r = 0; r < img->height; r++) {
        for (c = 0; c < img->width; c++) {
            pimg = (uchar*) img->imageData + r * img->widthStep + c;
            pbg = (uchar*) bgimg->data.ptr + r * bgimg->step + c;
            palpha = (uchar*) maskimg->imageData + r * maskimg->widthStep + c;
            chartmp = (uchar) MAX(0, MIN(255, forecolordev + (*pimg)));
            if (inverse) {
                chartmp ^= 0xFF;
            }
            *pbg = (uchar)((chartmp * (*palpha) + (255 - (*palpha)) * (*pbg)) / 255);
        }
    }

    cvReleaseImage(&img);
    cvReleaseImage(&maskimg);
}

void icvEndSampleDistortion(CvSampleDistortionData* data) {
    if (data->src) {
        cvReleaseImage(&data->src);
    }
    if (data->mask) {
        cvReleaseImage(&data->mask);
    }
    if (data->erode) {
        cvReleaseImage(&data->erode);
    }
    if (data->dilate) {
        cvReleaseImage(&data->dilate);
    }
    if (data->img) {
        cvReleaseImage(&data->img);
    }
    if (data->maskimg) {
        cvReleaseImage(&data->maskimg);
    }
}

void icvWriteVecHeader(FILE* file, int count, int width, int height) {
    int vecsize;
    short tmp;

    /* number of samples */
    fwrite(&count, sizeof(count), 1, file);
    /* vector size */
    vecsize = width * height;
    fwrite(&vecsize, sizeof(vecsize), 1, file);
    /* min/max values */
    tmp = 0;
    fwrite(&tmp, sizeof(tmp), 1, file);
    fwrite(&tmp, sizeof(tmp), 1, file);
}

void icvWriteVecSample(FILE* file, CvArr* sample) {
    CvMat* mat, stub;
    int r, c;
    short tmp;
    uchar chartmp;

    mat = cvGetMat(sample, &stub);
    chartmp = 0;
    fwrite(&chartmp, sizeof(chartmp), 1, file);
    for (r = 0; r < mat->rows; r++) {
        for (c = 0; c < mat->cols; c++) {
            tmp = (short)(CV_MAT_ELEM(*mat, uchar, r, c));
            fwrite(&tmp, sizeof(tmp), 1, file);
        }
    }
}


int cvCreateTrainingSamplesFromInfo(const char* infoname, const char* vecfilename,
                                    int num,
                                    int showsamples,
                                    int winwidth, int winheight) {
    char fullname[PATH_MAX];
    char* filename;

    FILE* info;
    FILE* vec;
    IplImage* src = 0;
    IplImage* sample;
    int line;
    int error;
    int i;
    int x, y, width, height;
    int total;

    assert(infoname != NULL);
    assert(vecfilename != NULL);

    total = 0;
    if (!icvMkDir(vecfilename)) {

#if CV_VERBOSE
        fprintf(stderr, "Unable to create directory hierarchy: %s\n", vecfilename);
#endif /* CV_VERBOSE */

        return total;
    }

    info = fopen(infoname, "r");
    if (info == NULL) {

#if CV_VERBOSE
        fprintf(stderr, "Unable to open file: %s\n", infoname);
#endif /* CV_VERBOSE */

        return total;
    }

    vec = fopen(vecfilename, "wb");
    if (vec == NULL) {

#if CV_VERBOSE
        fprintf(stderr, "Unable to open file: %s\n", vecfilename);
#endif /* CV_VERBOSE */

        fclose(info);

        return total;
    }

    sample = cvCreateImage(cvSize(winwidth, winheight), IPL_DEPTH_8U, 1);

    icvWriteVecHeader(vec, num, sample->width, sample->height);

    if (showsamples) {
        cvNamedWindow("Sample", CV_WINDOW_AUTOSIZE);
    }

    strcpy(fullname, infoname);
    filename = strrchr(fullname, '\\');
    if (filename == NULL) {
        filename = strrchr(fullname, '/');
    }
    if (filename == NULL) {
        filename = fullname;
    } else {
        filename++;
    }

    for (line = 1, error = 0, total = 0; total < num ; line++) {
        int count;

        error = (fscanf(info, "%s %d", filename, &count) != 2);
        if (!error) {
            src = cvLoadImage(fullname, 0);
            error = (src == NULL);
            if (error) {

#if CV_VERBOSE
                fprintf(stderr, "Unable to open image: %s\n", fullname);
#endif /* CV_VERBOSE */

            }
        }
        for (i = 0; (i < count) && (total < num); i++, total++) {
            error = (fscanf(info, "%d %d %d %d", &x, &y, &width, &height) != 4);
            if (error) { break; }
            cvSetImageROI(src, cvRect(x, y, width, height));
            cvResize(src, sample, width >= sample->width &&
                     height >= sample->height ? CV_INTER_AREA : CV_INTER_LINEAR);

            if (showsamples) {
                cvShowImage("Sample", sample);
                if (cvWaitKey(0) == 27) {
                    showsamples = 0;
                }
            }
            icvWriteVecSample(vec, sample);
        }

        if (src) {
            cvReleaseImage(&src);
        }

        if (error) {

#if CV_VERBOSE
            fprintf(stderr, "%s(%d) : parse error", infoname, line);
#endif /* CV_VERBOSE */

            break;
        }
    }

    if (sample) {
        cvReleaseImage(&sample);
    }

    fclose(vec);
    fclose(info);

    return total;
}


void cvShowVecSamples(const char* filename, int winwidth, int winheight,
                      double scale) {
    CvVecFile file;
    short tmp;
    int i;
    CvMat* sample;

    tmp = 0;
    file.input = fopen(filename, "rb");

    if (file.input != NULL) {
        fread(&file.count, sizeof(file.count), 1, file.input);
        fread(&file.vecsize, sizeof(file.vecsize), 1, file.input);
        fread(&tmp, sizeof(tmp), 1, file.input);
        fread(&tmp, sizeof(tmp), 1, file.input);

        if (file.vecsize != winwidth * winheight) {
            int guessed_w = 0;
            int guessed_h = 0;

            fprintf(stderr, "Warning: specified sample width=%d and height=%d "
                    "does not correspond to .vec file vector size=%d.\n",
                    winwidth, winheight, file.vecsize);
            if (file.vecsize > 0) {
                guessed_w = cvFloor(sqrt((float) file.vecsize));
                if (guessed_w > 0) {
                    guessed_h = file.vecsize / guessed_w;
                }
            }

            if (guessed_w <= 0 || guessed_h <= 0 || guessed_w* guessed_h != file.vecsize) {
                fprintf(stderr, "Error: failed to guess sample width and height\n");
                fclose(file.input);

                return;
            } else {
                winwidth = guessed_w;
                winheight = guessed_h;
                fprintf(stderr, "Guessed width=%d, guessed height=%d\n",
                        winwidth, winheight);
            }
        }

        if (!feof(file.input) && scale > 0) {
            CvMat* scaled_sample = 0;

            file.last = 0;
            file.vector = (short*) cvAlloc(sizeof(*file.vector) * file.vecsize);
            sample = scaled_sample = cvCreateMat(winheight, winwidth, CV_8UC1);
            if (scale != 1.0) {
                scaled_sample = cvCreateMat(MAX(1, cvCeil(scale * winheight)),
                                            MAX(1, cvCeil(scale * winwidth)),
                                            CV_8UC1);
            }
            cvNamedWindow("Sample", CV_WINDOW_AUTOSIZE);
            for (i = 0; i < file.count; i++) {
                icvGetHaarTraininDataFromVecCallback(sample, &file);
                if (scale != 1.0) { cvResize(sample, scaled_sample, CV_INTER_LINEAR); }
                cvShowImage("Sample", scaled_sample);
                if (cvWaitKey(0) == 27) { break; }
            }
            if (scaled_sample && scaled_sample != sample) { cvReleaseMat(&scaled_sample); }
            cvReleaseMat(&sample);
            cvFree(&file.vector);
        }
        fclose(file.input);
    }
}


/* End of file. */
